use std::{borrow::Cow, collections::HashSet, error::Error, iter, path::{Path, PathBuf}};

use bpf_linker::Linker;

pub fn linker<P: AsRef<Path>, T: AsRef<Path>>(input: &[P], output: T) -> Result<(), Box<dyn Error>> {
    let runtime = include_bytes!(concat!(
        env!("OUT_DIR"),
        "/runtime.bc"
    ));
    let runtime_bin = PathBuf::from("output/runtime.bc");
    std::fs::write(&runtime_bin, runtime)?;
    let mut export_symbols = HashSet::new();
    export_symbols.insert(Cow::Borrowed("ether_main"));
    export_symbols.insert(Cow::Borrowed("runtime"));
    let bpf_linker_opt = bpf_linker::LinkerOptions {
        target: Some("bpfel-unknown-none".to_string()),
        cpu: bpf_linker::Cpu::Generic,
        cpu_features: "".to_string(),
        inputs: input.iter().map(|p| p.as_ref().to_path_buf()).chain(iter::once(runtime_bin)).collect(),
        output: output.as_ref().to_path_buf(),
        output_type: bpf_linker::OutputType::Object,
        libs: vec![],
        optimize: bpf_linker::OptLevel::No,
        export_symbols,
        unroll_loops: false,
        ignore_inline_never: false,
        dump_module: None,
        llvm_args: vec![],
        disable_expand_memcpy_in_order: false,
        disable_memory_builtins: false,
        btf: false,
    };
    let mut bpf_linker = Linker::new(bpf_linker_opt);
    bpf_linker.link()?;
    Ok(())
}